let s:search_start=1
let s:last_line_num=-3
"""""""""""主函数"""""""""""
function! s:checkTag()
	let l:firstline=getline(1)
	if l:firstline[0]=="<"
		call append(0,"")
	endif
	"获取最后一个非空白行的行号,用于结束循环
	let l:lastLine=prevnonblank(line('$'))
	let l:remPos=[line('.'),col('.')]
	let l:wrong=0
	let l:T_stack=[]
	let l:Tag=["",""]
	"把光标移动到第一行的的行首
	call cursor(1,0)
	let l:times=0
	while 1
		let l:posL= s:fond_a_left()
		let l:ifTsign=s:ifTagSign(l:posL)
		"如果是标签的<
		if l:ifTsign == 1
			let l:Tag=s:get_a_Tag(l:posL)
			if l:Tag[1][1] != -1
				let l:ifaClose=s:if_a_close_tag(l:posL)
				"如果是个结束标签
				if l:ifaClose==1
					let l:stack_len=len(l:T_stack)
					if	l:stack_len>0
						"如果匹配
						if l:T_stack[0][0]==l:Tag[0]
						"移出堆栈
							call remove(l:T_stack,0)
						else
							echo "(".l:T_stack[0][1][0].",".l:T_stack[0][1][1]")处的<".l:T_stack[0][0].">少了与之对应的结束标签，或者(".l:Tag[1][0].",".l:Tag[1][1].")处多了一个</".l:Tag[0].">标签"
							let l:wrong=1
						endif
					else
						echo "第".l:Tag[1][0]."行多了一个</".l:Tag[0].">标签"
						let l:wrong=1
					endif
				else
					"压入堆栈
					let l:T_stack=insert(l:T_stack,l:Tag)
				endif
			else
			endif
		endif
		if  l:lastLine==l:posL[0]
			break
		endif
		"移动光标位置，保证循环正确的重要变量
		call cursor(l:posL[0],l:posL[1])
		let l:times=l:times+1
		if l:times>4000
			break
		endif
	endwhile
	if l:wrong==0
		for item in l:T_stack
			echo "(".item[1][0].",".item[1][1].")处的".item[0]."缺少与之对应的结束标签，可能的位置在".line('$')."，或者别处，情仔细检查"
			let l:wrong=1
		endfor
	endif
	if l:wrong==0
		echo "没有标签缺失,或多余"
	endif
	call cursor(l:remPos)
endfunction
"""""""""""从当前光标位置搜索最近的一个<，并把光标移动到它的位置，返回坐标"""""""""""
function! s:fond_a_left()
	"从光标位置查找最近的一个<
		let l:pos=searchpos("<",'n',line('$'))
	return l:pos
endfunction
	
""""""""""判断<之后的第一个非空字符是否为?　是的话则返回 1 ,否则返回 0。需要的参数为一个坐标类型为list""""""""""
function! s:ifTagSign(pos)
	let l:str=getline(a:pos[0])
	let l:test=strpart(l:str,a:pos[1],1)
	if l:test=="?"
		return 0
	else
		return 1
	endif
endfunction
""""""""""从光标位置往后寻找最近的>，返回坐标，并把光标移动到>的位置
function! s:fond_a_right(pos)
	let l:save_cursor=getpos('.')
	call cursor(a:pos[0],a:pos[1])
	"从光标位置查找>,查找范围为当前行
	let l:pos=searchpos(">",'n',line('.'))
	call cursor(l:save_cursor[1],l:save_cursor[2])
	return l:pos[1]
endfunction
"""""获取从< 到 > 之间的字符串，并与字符串进行匹配，需要的参数为起始与结束的列位置,返回这个tag的名称和它的坐标"""""
function! s:get_a_Tag(pos)
	"为了让第一次能被捕获，s:last_line_num已被赋初值-3
	if s:last_line_num != a:pos[0] 
		let s:search_start=1
	else
		let s:search_start=s:search_start+1
	endif
	let l:allTags='div \|div>\|ul \|ul>\|li \|li>\|span \|span>\|a \|a>\|p \|p>\|table \|table>\|tr \|tr>\|td \|td>'
	let l:strLine=getline(a:pos[0])
	"获得的 l:Tag_t 是标签名加之后的一个字符组成的字符串
	let l:Tag_t=matchstr(l:strLine,l:allTags,0,s:search_start)
	let l:tag_col=match(l:strLine,l:allTags,0,s:search_start)
	let l:test=strpart(l:strLine,0,l:tag_col)
	let l:len=strlen(l:test)
	"通过判断标签名之前的一个字符进行过滤，确定得到的是一个要检查的标签名
	if l:test[l:len-1] != "/"&&l:test[l:len-1] != " "&&l:test[l:len-1] != "<"
		let l:tag_col=-1
	endif
	"用 < 之后的 > 的列坐标来判断此次得到的标签是否正确，因为在一行中可能出现这种情况<span><img /></span>
	"并且<img />是不被检查的标签
	let l:right_col=s:fond_a_right(a:pos)
	if l:right_col != 0
		if l:tag_col>l:right_col
			let l:tag_col=-1
			let s:search_start=s:search_start-1
		endif
	endif
	let l:Tag=strpart(l:Tag_t,0,strlen(l:Tag_t)-1)
	let l:pos=[a:pos[0],l:tag_col]
	let l:value=[l:Tag,l:pos]
	let s:last_line_num=a:pos[0]
	return l:value
endfunction
"""""""""判断<之后的第一个字符是否为/，即判断是不是一个结束标签，包含的话返回1，否则返回0"""""""
function! s:if_a_close_tag(pos)
	let l:str1=getline(a:pos[0])
	let l:str2=strpart(l:str1,a:pos[1],1)
	if l:str2=="/"
		let l:value=1
	else
		let l:value=0
	endif
	return l:value
endfunction


nnoremap <silent> <leader>che :call <SID>checkTag()<cr>

